import store from '@/store'

const prod_url = 'www.qxclude.cn'
const dev_url = 'localhost:8150'

class Socket {
    wss = null
    url = ''
    port = null
    timeInterval = null
    timeOutInterval = null
    protocol = 'ws'
    count = 1 // 断线重连次数
    reconnectTimer = null // 断线重连定时器
    restartInterval = 5000 // 断线重连的间隔时间（毫秒）
    isTokenExpired = false // 添加token过期标志
    isRefreshTokenExpired = false // 添加refreshToken过期标志
    tokenRefreshListener = null // token刷新监听器
    isManualClose = false // 标记是否是手动关闭
    
    constructor(config = {}) {
        // WebSocket 默认配置信息
        const defaultConfig = {
            url: process.env.NODE_ENV === 'development' ? prod_url : prod_url,
            port: 9000,
            timeOutInterval: 10 * 1000 + 1000,
            protocol: process.env.NODE_ENV === 'development' ? 'wss' : 'wss',
        }

        const finalConfig = { ...defaultConfig, ...config }

        this.url = finalConfig.url
        this.port = finalConfig.port
        this.timeOutInterval = finalConfig.timeOutInterval
        this.protocol = finalConfig.protocol

        // 监听store中token的变化
        this.setupTokenListener()
    }

    // 设置token变化监听器
    setupTokenListener() {
        // 监听store中token的变化，当token刷新后自动重连
        this.tokenRefreshListener = store.subscribe((mutation, state) => {
            // 当accessToken刷新成功时
            if (mutation.type === 'user/setToken' && this.isTokenExpired && !this.isRefreshTokenExpired) {
                this.isTokenExpired = false
                
                // token刷新后重新连接WebSocket
                setTimeout(() => {
                    this.init()
                }, 1000)
            }
            
            // 监听退出登录
            if (mutation.type === 'user/logout') {
                this.isTokenExpired = true
                this.isRefreshTokenExpired = true
                this.isManualClose = true
                this.handleClose()
            }
        })
    }

    // 初始化 WebSocket 服务
    init() {
        // 如果用户未登录，或者refreshToken已过期，则不初始化 WebSocket 服务
        if (!store.state.user || !store.state.user.token || this.isRefreshTokenExpired) {
            return
        }

        // 如果当前WebSocket 服务已存在，并且已连接，则直接返回
        if (this.wss && this.wss.readyState === WebSocket.OPEN) return

        // 重置手动关闭标志
        this.isManualClose = false

        try {
            this.wss = new WebSocket(`${ this.protocol }://${ this.url }/ws/?token=${ store.state.user.token }&type=front`) // 创建 WebSocket 服务
            this.wss.onopen = () => this.handleWsOpen() // 监听 WebSocket 连接
            this.wss.onclose = (ws) => this.handleWsClose(ws) // 监听 WebSocket 断开
            this.wss.onerror = (ws) => this.handleWsError(ws) // 监听 WebSocket 连接失败
            this.wss.onmessage = (event) => this.onMessage(event) // 监听服务端消息
        } catch (error) {
            console.error('WebSocket 创建失败：', error)
        }
    }

    // 添加发送消息的方法
    sendMessage(event) {
        if (this.wss && this.wss.readyState === WebSocket.OPEN) {
            this.wss.send(JSON.stringify(event))
        } else {
            console.error('WebSocket 连接未就绪，无法发送消息')
        }
    }

    // 监听 WebSocket 连接
    handleWsOpen() {
        // 连接成功后，重连定时器清除
        clearTimeout(this.reconnectTimer)
        this.reconnectTimer = null
        this.count = 1 // 重置重连次数
        this.isTokenExpired = false // 重置token过期状态
        console.log('WebSocket 连接成功')
    }

    // 监听 WebSocket 断开
    handleWsClose(event) {
        // 如果是手动关闭，不进行重连
        if (this.isManualClose) {
            return
        }
        
        // 检测是否是 Token 过期导致的断开
        if (event.code === 1008 || 
            (event.reason && (event.reason.includes('token') || event.reason.includes('expired') || event.reason.includes('auth')))) {
            console.log('WebSocket 因 Token 过期断开')
            this.handleTokenExpired()
            return
        }
        
        // 非token过期的正常断开，只有在refreshToken未过期时才进行重连
        if (!this.isRefreshTokenExpired) {
            this.handleReconnect()
        }
    }

    // 处理token过期
    handleTokenExpired() {
        this.isTokenExpired = true
        
        // 只有在refreshToken未过期时才标记为token过期
        // 如果refreshToken已过期，说明双token都已过期，不重复处理
        if (!this.isRefreshTokenExpired) {
            console.log('WebSocket token过期，等待刷新')
        } else {
            console.log('双token都已过期，WebSocket停止重连')
        }
    }

    // 主动关闭socket
    handleClose() {
        this.isManualClose = true
        if (this.wss) {
            this.wss.close()
            this.wss = null
        }
        clearTimeout(this.reconnectTimer)
        clearTimeout(this.timeInterval)
    }

    // 监听 WebSocket 连接失败
    handleWsError(event) {
        console.error('WebSocket 错误:', event)
        
        // 如果是手动关闭，不处理错误
        if (this.isManualClose) {
            return
        }
        
        // 检查错误信息中是否包含token相关提示
        const errorMessage = event?.message || event?.target?.url || ''
        if (errorMessage.includes('token') || errorMessage.includes('auth') || errorMessage.includes('expired')) {
            this.handleTokenExpired()
        } else if (!this.isTokenExpired && !this.isRefreshTokenExpired) {
            // 非token相关的错误且refreshToken未过期才进行重连
            this.handleReconnect()
        }
    }

    // 断线重连
    handleReconnect() {
        // 如果 Token 已过期或refreshToken已过期，不进行重连
        if (this.isTokenExpired || this.isRefreshTokenExpired || this.isManualClose) {
            console.log('Token 已过期或手动关闭，停止重连')
            return
        }

        // 每一次重连都需要将上一次的定时器清除
        clearTimeout(this.reconnectTimer)
        this.reconnectTimer = null
        // 每一次重连都需要将上最后一次的心跳定时器清除
        clearTimeout(this.timeInterval)
        this.timeInterval = null

        // 如果重连次数大于等于 10 次，则放弃重连
        if (this.count >= 10) {
            this.count = 1
            console.log('消息重连超时，请联系管理员！')
            
            // 显示错误提示
            if (Utils && Utils.popUpMsg) {
                Utils.popUpMsg('error', '网络连接异常，请检查网络后重试', 3000)
            }
            return
        }

        // 如果重连次数小于 10 次，一直创建连接，每次重连都将次数 + 1
        this.reconnectTimer = setTimeout(() => {
            this.count += 1
            console.log(`第 ${this.count} 次尝试重连 WebSocket`)
            this.init()
        }, this.restartInterval)
    }

    // 监听服务端消息，添加 Token 过期处理
    onMessage(event) {
        try {
            let data = JSON.parse(event.data)

            switch (data.type) {
                case 'noAuth':
                    console.log('WebSocket 鉴权失败！', data)
                    this.handleTokenExpired()
                    
                    // 关闭当前连接
                    this.handleClose()
                    break;
                    
                case 'heartbeat':
                    this.checkServer()
                    if (this.wss && this.wss.readyState === WebSocket.OPEN) {
                        this.wss.send(JSON.stringify({
                            type: 'heartbeat',
                            msg: 'pong'
                        }))
                    }
                    break;
                    
                default:
                    window.dispatchEvent(new CustomEvent('onMessage', { detail: data }))
                    break;
            }
        } catch (error) {
            console.error('处理 WebSocket 消息失败：', error)
        }
    }

    // 检测心跳是否超时，如果超时，断开 socket 重新连接
    checkServer() {
        clearTimeout(this.timeInterval)
        this.timeInterval = setTimeout(() => {
            // 只有在 Token 未过期且refreshToken未过期且非手动关闭时才重连
            if (!this.isTokenExpired && !this.isRefreshTokenExpired && !this.isManualClose && this.wss && this.wss.readyState !== WebSocket.OPEN) {
                this.handleClose()
                this.handleReconnect()
            }
        }, this.timeOutInterval)
    }

    // 销毁方法，清理资源
    destroy() {
        this.handleClose()
        
        // 移除 store 监听器
        if (this.tokenRefreshListener) {
            this.tokenRefreshListener()
            this.tokenRefreshListener = null
        }
    }
}

export default Socket